<!DOCTYPE html>
<html lang="cn">

<head>
  <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="Shell 什么是shell 现代计算机有越来越多的GUI程序、语音输入程序、甚至VR、AR程序，这些交互方式能解决我们80%的使用场景，但它们还是从根本上限制了我们使用计算机的方式，你没法点击一个不存在的按钮。为了充分利用计算机的能力，我们不得不回到最根本的方式，即shell。
所以，shell也是一种交互方式，有很多种类型的shell，例如系统自带的Windows的powershell、Linux的bash(Bourne-Again SHell)，还有开发者编写的zsh(Z Shell)、fish等都比较常见。
基础使用 基础技巧 $表示当前不是root用户，#表示当前是root用户。
除了内置的函数，shell通过递归$PATH中的目录找到合适的执行程序: [ubuntu] / $ echo $PATH /usr/local/go/bin:/root/go/bin:/usr/local/go/bin:/root/go/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin [ubuntu] / $ which echo /bin/echo [ubuntu] / $ /bin/echo $PATH /usr/local/go/bin:/root/go/bin:/usr/local/go/bin:/root/go/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
当传递的参数包含空格时，应该使用单引号或双引号将其包起来，或者使用\转义一下，否则会被认为是两个参数: [ubuntu] ~ $ mkdir My Photo [ubuntu] ~ $ ls My Photo go1.13.linux-amd64.tar.gz [ubuntu] ~ $ mkdir My\ Photo [ubuntu] ~ $ ls My &#39;My Photo&#39; Photo go1.13.linux-amd64.tar.gz
/表示根目录，~表示home目录，.表示当前目录，..表示上级目录，还可以使用-来切换目录: [ubuntu] ~ $ cd /tmp [ubuntu] /tmp $ cd - /root [ubuntu] ~ $ cd - /tmp"><meta property="og:title" content="Shell" />
<meta property="og:description" content="" />
<meta property="og:type" content="website" />
<meta property="og:url" content="/docs/other/shell/" />

<title>Shell | Home</title>
<link rel="icon" href="/favicon.png" type="image/x-icon">
<link rel="stylesheet" href="/book.min.63eb88daa545365405ecdbb21033286a325c60a36cfa6d22d21e7c3bc9286941.css" integrity="sha256-Y&#43;uI2qVFNlQF7NuyEDMoajJcYKNs&#43;m0i0h58O8koaUE=">
<script defer src="/cn.search.min.8d2462c3b745732864d843e8e2e7782a2ec838ca90fb94676baa461536bdae11.js" integrity="sha256-jSRiw7dFcyhk2EPo4ud4Ki7IOMqQ&#43;5Rna6pGFTa9rhE="></script>
<link rel="alternate" type="application/rss+xml" href="/docs/other/shell/index.xml" title="Home" />
<!--
Made with Book Theme
https://github.com/alex-shpak/hugo-book
-->

  
</head>

<body>
  <input type="checkbox" class="hidden" id="menu-control" />
  <main class="container flex">
    <aside class="book-menu">
      
  <nav>
<h2 class="book-brand">
  <a href="/"><img src="/logo.png" alt="Logo" /><span>Home</span>
  </a>
</h2>


<div class="book-search">
  <input type="text" id="book-search-input" placeholder="搜索" aria-label="搜索" maxlength="64" data-hotkeys="s/" />
  <div class="book-search-spinner spinner hidden"></div>
  <ul id="book-search-results"></ul>
</div>











  <ul>
<li><strong>计算机基础</strong>
<ul>
<li><a href="/docs/sicp/hardware/">硬件</a></li>
<li><a href="/docs/sicp/software/">软件</a></li>
<li><a href="/docs/sicp/program/">程序</a></li>
<li><a href="/docs/sicp/asm/">汇编</a></li>
</ul>
</li>
<li><strong>GO语言</strong>
<ul>
<li><a href="/docs/go/map/">字典</a></li>
<li><a href="/docs/go/closure/">闭包</a></li>
<li><a href="/docs/go/defer/">延迟调用</a></li>
<li><a href="/docs/go/goroutine/">并发调度</a></li>
<li><a href="/docs/go/alloc/">内存分配</a></li>
<li><a href="/docs/go/gc/">垃圾回收</a></li>
<li><a href="/docs/go/lock/">锁</a></li>
</ul>
</li>
<li><strong>Python语言</strong>
<ul>
<li><a href="/docs/python/memory/">内存管理</a></li>
<li><a href="/docs/python/interpreter/">解释器</a></li>
<li><a href="/docs/python/tools/">技巧工具</a></li>
</ul>
</li>
<li><strong>Mysql</strong>
<ul>
<li><a href="/docs/mysql/query/">查询</a></li>
<li><a href="/docs/mysql/theory/">原理</a></li>
</ul>
</li>
<li><strong>其他</strong>
<ul>
<li><a href="/docs/other/git/">git</a></li>
<li><a href="/docs/other/docker/">docker</a></li>
<li><a href="/docs/other/raft/">raft</a></li>
<li><a href="/docs/other/shell/"class=active>shell</a></li>
<li><a href="/docs/other/oop/">面向对象</a></li>
<li><a href="/docs/other/protocol/">网络协议</a></li>
<li><a href="/docs/other/tools/">系统工具</a></li>
</ul>
</li>
</ul>










</nav>




  <script>(function(){var menu=document.querySelector("aside.book-menu nav");addEventListener("beforeunload",function(event){localStorage.setItem("menu.scrollTop",menu.scrollTop);});menu.scrollTop=localStorage.getItem("menu.scrollTop");})();</script>


 
    </aside>

    <div class="book-page">
      <header class="book-header">
        
  <div class="flex align-center justify-between">
  <label for="menu-control">
    <img src="/svg/menu.svg" class="book-icon" alt="Menu" />
  </label>

  <strong>Shell</strong>

  <label for="toc-control">
    <img src="/svg/toc.svg" class="book-icon" alt="Table of Contents" />
  </label>
</div>


  
    <input type="checkbox" class="hidden" id="toc-control" />
    <aside class="hidden clearfix">
      
  <nav id="TableOfContents">
  <ul>
    <li><a href="#什么是shell">什么是shell</a></li>
    <li><a href="#基础使用">基础使用</a>
      <ul>
        <li><a href="#基础技巧">基础技巧</a></li>
        <li><a href="#程序间的连接">程序间的连接</a></li>
        <li><a href="#root">root</a></li>
      </ul>
    </li>
    <li><a href="#编写脚本">编写脚本</a>
      <ul>
        <li><a href="#变量">变量</a></li>
        <li><a href="#函数">函数</a></li>
        <li><a href="#命令替换">命令替换</a></li>
        <li><a href="#控制流">控制流</a></li>
        <li><a href="#通配符">通配符</a></li>
        <li><a href="#脚本">脚本</a></li>
      </ul>
    </li>
    <li><a href="#任务控制">任务控制</a></li>
    <li><a href="#常用工具">常用工具</a>
      <ul>
        <li><a href="#find">find</a></li>
        <li><a href="#grep">grep</a></li>
      </ul>
    </li>
  </ul>
</nav>


    </aside>
  
 
      </header>

      
      
  <article class="markdown"><h1 id="shell">Shell</h1>
<h2 id="什么是shell">什么是shell</h2>
<p>现代计算机有越来越多的GUI程序、语音输入程序、甚至VR、AR程序，这些交互方式能解决我们80%的使用场景，但它们还是从根本上限制了我们使用计算机的方式，你没法点击一个不存在的按钮。为了充分利用计算机的能力，我们不得不回到最根本的方式，即<strong>shell</strong>。</p>
<p>所以，shell也是一种交互方式，有很多种类型的shell，例如系统自带的Windows的powershell、Linux的bash(Bourne-Again SHell)，还有开发者编写的zsh(Z Shell)、fish等都比较常见。</p>
<h2 id="基础使用">基础使用</h2>
<h3 id="基础技巧">基础技巧</h3>
<p><code>$</code>表示当前不是root用户，<code>#</code>表示当前是root用户。</p>
<p>除了内置的函数，shell通过递归<code>$PATH</code>中的目录找到合适的执行程序:
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-sh" data-lang="sh"><span style="color:#f92672">[</span>ubuntu<span style="color:#f92672">]</span> / $ echo $PATH
/usr/local/go/bin:/root/go/bin:/usr/local/go/bin:/root/go/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
<span style="color:#f92672">[</span>ubuntu<span style="color:#f92672">]</span> / $ which echo
/bin/echo
<span style="color:#f92672">[</span>ubuntu<span style="color:#f92672">]</span> / $ /bin/echo $PATH
/usr/local/go/bin:/root/go/bin:/usr/local/go/bin:/root/go/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin</code></pre></div></p>
<p>当传递的参数包含空格时，应该使用单引号或双引号将其包起来，或者使用<code>\</code>转义一下，否则会被认为是两个参数:
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-sh" data-lang="sh"><span style="color:#f92672">[</span>ubuntu<span style="color:#f92672">]</span> ~ $ mkdir My Photo
<span style="color:#f92672">[</span>ubuntu<span style="color:#f92672">]</span> ~ $ ls
My  Photo  go1.13.linux-amd64.tar.gz
<span style="color:#f92672">[</span>ubuntu<span style="color:#f92672">]</span> ~ $ mkdir My<span style="color:#ae81ff">\ </span>Photo
<span style="color:#f92672">[</span>ubuntu<span style="color:#f92672">]</span> ~ $ ls
 My  <span style="color:#e6db74">&#39;My Photo&#39;</span>   Photo   go1.13.linux-amd64.tar.gz</code></pre></div></p>
<p><code>/</code>表示根目录，<code>~</code>表示home目录，<code>.</code>表示当前目录，<code>..</code>表示上级目录，还可以使用<code>-</code>来切换目录:
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-sh" data-lang="sh"><span style="color:#f92672">[</span>ubuntu<span style="color:#f92672">]</span> ~ $ cd /tmp
<span style="color:#f92672">[</span>ubuntu<span style="color:#f92672">]</span> /tmp $ cd -
/root
<span style="color:#f92672">[</span>ubuntu<span style="color:#f92672">]</span> ~ $ cd -
/tmp</code></pre></div></p>
<h3 id="程序间的连接">程序间的连接</h3>
<p>在shell中，程序都有两个主要的流，即输入流和输出流。当程序需要读取数据时就是从输入流读取，这通常是你的键盘；当程序需要打印信息时，它会将信息放在输出流中，这通常是你的屏幕。然而我们也可以修改默认的输入输出流。</p>
<p>例如，通过<code>&gt; file</code>我们可以把输出流的内容放在一个文件中，通过<code>&lt; file</code>我们可以从一个文件中读取内容作为输入流:
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-sh" data-lang="sh"><span style="color:#f92672">[</span>ubuntu<span style="color:#f92672">]</span> /tmp $ echo <span style="color:#e6db74">&#39;hello world&#39;</span> &gt; hello.txt
<span style="color:#f92672">[</span>ubuntu<span style="color:#f92672">]</span> /tmp $ cat &lt; hello.txt &gt; hello2.txt
<span style="color:#f92672">[</span>ubuntu<span style="color:#f92672">]</span> /tmp $ cat hello2.txt
hello world</code></pre></div>
也可以通过<code>&gt;&gt; file</code>把输出流的内容追加到文件的尾部，通过<code>|</code>把前面一个程序的输出作为后一个程序的输入:
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-sh" data-lang="sh"><span style="color:#f92672">[</span>ubuntu<span style="color:#f92672">]</span> /tmp $ echo <span style="color:#ae81ff">1234</span> &gt;&gt; hello2.txt
<span style="color:#f92672">[</span>ubuntu<span style="color:#f92672">]</span> /tmp $ cat hello2.txt
hello world
<span style="color:#ae81ff">1234</span>
<span style="color:#f92672">[</span>ubuntu<span style="color:#f92672">]</span> /tmp $ ls -l | tail -n1
drwx------  <span style="color:#ae81ff">2</span> root root    <span style="color:#ae81ff">4096</span> Dec  <span style="color:#ae81ff">6</span>  <span style="color:#ae81ff">2019</span> tmux-0
<span style="color:#f92672">[</span>ubuntu<span style="color:#f92672">]</span> /tmp $ ls -l | tail -n3
-rw-r--r--  <span style="color:#ae81ff">1</span> root root       <span style="color:#ae81ff">1</span> Jun <span style="color:#ae81ff">15</span> 10:13 hello3.txt
-rwxr-xr-x  <span style="color:#ae81ff">1</span> root root <span style="color:#ae81ff">1148709</span> Dec  <span style="color:#ae81ff">9</span>  <span style="color:#ae81ff">2019</span> test
drwx------  <span style="color:#ae81ff">2</span> root root    <span style="color:#ae81ff">4096</span> Dec  <span style="color:#ae81ff">6</span>  <span style="color:#ae81ff">2019</span> tmux-0</code></pre></div></p>
<h3 id="root">root</h3>
<p>在大多数类Unix系统中，<code>root</code>用户都是很特殊的用户，它几乎可以不受限制的创建、读取、修改和删除系统中的任意文件。所以我们为避免对系统造成一定的破坏，通常不会使用root用户去登录，取而代之的是在需要的时候使用<code>sudo</code>命令。</p>
<p>例如，Linux系统的笔记本的屏幕亮度是写在<code>/sys/class/backlight</code>文件中，我们通过修改它可以修改屏幕亮度，这就需要<code>sudo</code>，我们可能做这样的尝试:
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-sh" data-lang="sh">$ cd /sys/class/backlight/thinkpad_screen
$ sudo echo <span style="color:#ae81ff">3</span> &gt; brightness
An error occurred <span style="color:#66d9ef">while</span> redirecting file <span style="color:#e6db74">&#39;brightness&#39;</span>
open: Permission denied</code></pre></div>
我们依然会得到错误的没有权限的提示，这是因为shell中<code>|</code>,<code>&gt;</code>,<code>&lt;</code>，各个具体的程序是不知道他们的存在的，echo只知道把内容写入到某个输入流中，具体哪个它不知道，所以我们应该使用:
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-sh" data-lang="sh">$ echo <span style="color:#ae81ff">3</span> | sudo tee brightness</code></pre></div>
这样打开brightness的是tee这个程序，且这个程序是有root权限的，才能正确的修改。</p>
<h2 id="编写脚本">编写脚本</h2>
<p>大多数shell都有自己的一套脚本语言，包括变量、控制流和自己的语法。本文主要使用<code>bash</code>脚本，它是目前应用最广泛的脚本。</p>
<h3 id="变量">变量</h3>
<p>可以使用<code>foo=bar</code>来定义变量foo，但要注意空格。输出变量值时，单引号和双引号也有区别:
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-sh" data-lang="sh"><span style="color:#f92672">[</span>ubuntu<span style="color:#f92672">]</span> /tmp/missing $ foo <span style="color:#f92672">=</span> bar
bash: foo: command not found
<span style="color:#f92672">[</span>ubuntu<span style="color:#f92672">]</span> /tmp/missing $ foo<span style="color:#f92672">=</span>bar
<span style="color:#f92672">[</span>ubuntu<span style="color:#f92672">]</span> /tmp/missing $ echo <span style="color:#e6db74">&#39;this is $foo&#39;</span>
this is $foo
<span style="color:#f92672">[</span>ubuntu<span style="color:#f92672">]</span> /tmp/missing $ echo <span style="color:#e6db74">&#34;this is </span>$foo<span style="color:#e6db74">&#34;</span>
this is bar</code></pre></div></p>
<p>同时，bash中也提供了一些特殊的变量:</p>
<ul>
<li><code>$0</code>，脚本名</li>
<li><code>$1</code>至<code>$9</code>，脚本的第几个参数</li>
<li><code>$@</code>，脚本的所有参数</li>
<li><code>$#</code>，参数的个数</li>
<li><code>$?</code>，前一个命令的返回值</li>
<li><code>$$</code>，返回当前脚本的进程ID(PID)</li>
<li><code>!!</code>，表示上一条命令，提示权限不足时可以使用<code>sudo !!</code>再次执行</li>
<li><code>$_</code>，上一条命令的最后一个参数</li>
</ul>
<p>执行一条命令后，通常使用STDOUT返回输出的值，STDERR返回错误及错误码，正常执行的错误码是0，也就是<code>$?</code>得到的值。</p>
<h3 id="函数">函数</h3>
<p>可以在一个脚本中编写函数:
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-sh" data-lang="sh">mcd <span style="color:#f92672">()</span> <span style="color:#f92672">{</span>
    mkdir -p <span style="color:#e6db74">&#34;</span>$1<span style="color:#e6db74">&#34;</span>
    cd <span style="color:#e6db74">&#34;</span>$1<span style="color:#e6db74">&#34;</span>
<span style="color:#f92672">}</span></code></pre></div></p>
<p>然后使用source导入就可以使用该函数:
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-sh" data-lang="sh"><span style="color:#f92672">[</span>ubuntu<span style="color:#f92672">]</span> /tmp/missing $ source ./test.sh
<span style="color:#f92672">[</span>ubuntu<span style="color:#f92672">]</span> /tmp/missing $ mcd tt
<span style="color:#f92672">[</span>ubuntu<span style="color:#f92672">]</span> /tmp/missing/tt $</code></pre></div></p>
<h3 id="命令替换">命令替换</h3>
<p>可以通过<code>$(CMD)</code>的方式先来执行CMD，执行得到的结果替换掉$(CMD)。例如，<code>for file in $(ls)</code>使shell首先调用ls，然后遍历得到的这些返回值。此外，还可以通过<code>&lt;(CMD)</code>去先执行CMD，然后将结果输出到一个临时文件中，并将&lt;(CMD)替换成临时文件名。
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-sh" data-lang="sh"><span style="color:#f92672">[</span>ubuntu<span style="color:#f92672">]</span> /tmp $ echo <span style="color:#e6db74">&#34;Starting program at </span><span style="color:#66d9ef">$(</span>date<span style="color:#66d9ef">)</span><span style="color:#e6db74">&#34;</span>
Starting program at Wed Jun <span style="color:#ae81ff">17</span> 16:13:34 CST <span style="color:#ae81ff">2020</span>
<span style="color:#f92672">[</span>ubuntu<span style="color:#f92672">]</span> /tmp $ diff &lt;<span style="color:#f92672">(</span>ls go<span style="color:#f92672">)</span> &lt;<span style="color:#f92672">(</span>ls missing<span style="color:#f92672">)</span>
1,18c1,4
&lt; AUTHORS
&lt; CONTRIBUTING.md
...
---
&gt; <span style="color:#ae81ff">3</span>
&gt; semester
&gt; test.sh
&gt; tt</code></pre></div></p>
<h3 id="控制流">控制流</h3>
<p>我们先来看一个例子:
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-sh" data-lang="sh"><span style="color:#75715e">#!/bin/bash
</span><span style="color:#75715e"></span>
echo <span style="color:#e6db74">&#34;Starting program at </span><span style="color:#66d9ef">$(</span>date<span style="color:#66d9ef">)</span><span style="color:#e6db74">&#34;</span> <span style="color:#75715e"># Date will be substituted</span>

echo <span style="color:#e6db74">&#34;Running program </span>$0<span style="color:#e6db74"> with </span>$#<span style="color:#e6db74"> arguments with pid </span>$$<span style="color:#e6db74">&#34;</span>

<span style="color:#66d9ef">for</span> file in <span style="color:#e6db74">&#34;</span>$@<span style="color:#e6db74">&#34;</span>; <span style="color:#66d9ef">do</span>
    grep foobar <span style="color:#e6db74">&#34;</span>$file<span style="color:#e6db74">&#34;</span> &gt; /dev/null 2&gt; /dev/null
    <span style="color:#75715e"># When pattern is not found, grep has exit status 1</span>
    <span style="color:#75715e"># We redirect STDOUT and STDERR to a null register since we do not care about them</span>
    <span style="color:#66d9ef">if</span> <span style="color:#f92672">[[</span> $? -ne <span style="color:#ae81ff">0</span> <span style="color:#f92672">]]</span>; <span style="color:#66d9ef">then</span>
        echo <span style="color:#e6db74">&#34;File </span>$file<span style="color:#e6db74"> does not have any foobar, adding one&#34;</span>
        echo <span style="color:#e6db74">&#34;# foobar&#34;</span> &gt;&gt; <span style="color:#e6db74">&#34;</span>$file<span style="color:#e6db74">&#34;</span>
    <span style="color:#66d9ef">fi</span>
<span style="color:#66d9ef">done</span></code></pre></div>
例如，运行<code>./test.sh a b c</code>，则遍历a、b、c三个文件，分别去寻找<code>foobar</code>字符串，如果没找到，错误返回值就是1。接着使用<code>-ne</code>来做比较<code>$?</code>不等于0，则把<code># foobar</code>追加到这个文件中。</p>
<p>注意，做比较时尽量使用双方括号<code>[[]]</code>，而非单方括号，可以降低犯错的几率。具体的原因和其他比较条件可参考<a href="http://mywiki.wooledge.org/BashFAQ/031">此文章</a>。</p>
<h3 id="通配符">通配符</h3>
<p>在shell中，可能经常需要输入一批类似的参数，例如<code>mkdir foo1 foo2 foo3</code>等等，如果挨个输入会很不方便，因此有了通配符。</p>
<p>可以分别使用<code>?</code>和<code>*</code>来匹配一个或任意个字符，花括号<code>{}</code>中可以包含一系列的指令。
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-sh" data-lang="sh"><span style="color:#f92672">[</span>ubuntu<span style="color:#f92672">]</span> /tmp/missing/tt $ touch foo<span style="color:#f92672">{</span>1..20<span style="color:#f92672">}</span>.txt
<span style="color:#f92672">[</span>ubuntu<span style="color:#f92672">]</span> /tmp/missing/tt $ ls
foo1.txt   foo11.txt  foo13.txt  foo15.txt  foo17.txt  foo19.txt  foo20.txt  foo4.txt  foo6.txt  foo8.txt
foo10.txt  foo12.txt  foo14.txt  foo16.txt  foo18.txt  foo2.txt   foo3.txt   foo5.txt  foo7.txt  foo9.txt
<span style="color:#f92672">[</span>ubuntu<span style="color:#f92672">]</span> /tmp/missing/tt $ rm foo?
rm: cannot remove <span style="color:#e6db74">&#39;foo?&#39;</span>: No such file or directory
<span style="color:#f92672">[</span>ubuntu<span style="color:#f92672">]</span> /tmp/missing/tt $ rm foo?.txt
<span style="color:#f92672">[</span>ubuntu<span style="color:#f92672">]</span> /tmp/missing/tt $ ls
foo10.txt  foo11.txt  foo12.txt  foo13.txt  foo14.txt  foo15.txt  foo16.txt  foo17.txt  foo18.txt  foo19.txt  foo20.txt
<span style="color:#f92672">[</span>ubuntu<span style="color:#f92672">]</span> /tmp/missing/tt $ rm foo*
<span style="color:#f92672">[</span>ubuntu<span style="color:#f92672">]</span> /tmp/missing/tt $ ls
<span style="color:#f92672">[</span>ubuntu<span style="color:#f92672">]</span> /tmp/missing/tt $ mkdir <span style="color:#f92672">{</span>foo,bar<span style="color:#f92672">}</span><span style="color:#ae81ff">123</span>
<span style="color:#f92672">[</span>ubuntu<span style="color:#f92672">]</span> /tmp/missing/tt $ ls
bar123  foo123
<span style="color:#f92672">[</span>ubuntu<span style="color:#f92672">]</span> /tmp/missing/tt $ touch <span style="color:#f92672">{</span>foo,bar<span style="color:#f92672">}</span>123/<span style="color:#f92672">{</span>a..h<span style="color:#f92672">}</span>
<span style="color:#f92672">[</span>ubuntu<span style="color:#f92672">]</span> /tmp/missing/tt $ ls bar123
a  b  c  d  e  f  g  h</code></pre></div></p>
<h3 id="脚本">脚本</h3>
<p>脚本也可以使用其他语言编写，只要标记好<code>shebang</code>，例如:
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-python" data-lang="python"><span style="color:#75715e">#!/usr/local/bin/python</span>
<span style="color:#f92672">import</span> sys
<span style="color:#66d9ef">for</span> arg <span style="color:#f92672">in</span> reversed(sys<span style="color:#f92672">.</span>argv[<span style="color:#ae81ff">1</span>:]):
    <span style="color:#66d9ef">print</span>(arg)</code></pre></div></p>
<p>写完以后要赋予它权限，才能正确执行:
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-sh" data-lang="sh"><span style="color:#f92672">[</span>ubuntu<span style="color:#f92672">]</span> /tmp/missing/tt $ ./test.py
bash: ./test.py: Permission denied
<span style="color:#f92672">[</span>ubuntu<span style="color:#f92672">]</span> /tmp/missing/tt $ chmod a+x test.py
<span style="color:#f92672">[</span>ubuntu<span style="color:#f92672">]</span> /tmp/missing/tt $ ./test.py a n cs
cs
n
a</code></pre></div></p>
<p>但是其他电脑的python程序并不一定位于<code>/usr/local/bin/python</code>，如何让这个脚本通用呢？我们可以利用环境变量中的程序来解析该脚本，将shebang信息改为<code>#!/usr/bin/env python</code>即可。</p>
<p>此外，shell函数和脚本是有一些不同点的:</p>
<ul>
<li>函数只能用shell语言，脚本可以用任意语言，只要脚本中包含shebang就行</li>
<li>函数仅在定义时被加载，脚本是在每次执行时加载的。所以函数的加载略快，但每次修改得自己手动重新加载</li>
<li>函数会在当前的shell环境中执行，脚本会在单独的进程中执行</li>
<li>函数可以对环境变量直接进行修改，脚本则只能使用<code>export</code>将环境变量导出</li>
</ul>
<p><strong>那么使用<code>source script.sh</code>和<code>./script.sh</code>执行脚本有什么区别呢？</strong><br>
实际上使用source命令是在当前shell的session中执行脚本，该脚本中执行的任何对当前环境的更改都会留存在当前会话中；而如果直接执行./script.sh的话，当前的shell会新建一个新的session，并在该session中执行命令，执行完成后对原session没有影响。</p>
<h2 id="任务控制">任务控制</h2>
<p>shell使用UNIX提供的信号机制来执行进程间的通信。当一个进程接收到信号时，它会停止执行、处理该信号并基于信号传递的信息来改变其执行，所以信号相当于是软件中断。</p>
<p>例如当我们在键盘上输入<code>Ctrl-C</code>时，shell会发送一个<code>SIGINT</code>信号到进程，来终止这个程序。当然，进程中是可以捕获信号去做别的处理的，例如:
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-python" data-lang="python"><span style="color:#75715e">#!/usr/bin/env python</span>
<span style="color:#f92672">import</span> signal<span style="color:#f92672">,</span> time

<span style="color:#66d9ef">def</span> <span style="color:#a6e22e">handler</span>(signum, time):
    <span style="color:#66d9ef">print</span>(<span style="color:#e6db74">&#34;</span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">I got a SIGINT, but I am not stopping&#34;</span>)

signal<span style="color:#f92672">.</span>signal(signal<span style="color:#f92672">.</span>SIGINT, handler)
i <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>
<span style="color:#66d9ef">while</span> True:
    time<span style="color:#f92672">.</span>sleep(<span style="color:#f92672">.</span><span style="color:#ae81ff">1</span>)
    <span style="color:#66d9ef">print</span>(<span style="color:#e6db74">&#34;</span><span style="color:#ae81ff">\r</span><span style="color:#e6db74">{}&#34;</span><span style="color:#f92672">.</span>format(i), end<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;&#34;</span>)
    i <span style="color:#f92672">+=</span> <span style="color:#ae81ff">1</span></code></pre></div>
此时，我们通过<code>Ctrl-C</code>就已经无法终止该程序了:
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-sh" data-lang="sh">$ python sigint.py
24^C
I got a SIGINT, but I am not stopping
26^C
I got a SIGINT, but I am not stopping
30^<span style="color:#ae81ff">\[</span>1<span style="color:#f92672">]</span>    <span style="color:#ae81ff">39913</span> quit       python sigint.py</code></pre></div>
我们可以通过发送其他中断信号来停止该程序，例如<code>SIGQUIT</code>或<code>SIGTERM</code>等。那么如果一个程序捕获了所有的信号，是不是它就无法被外部停止了？实际上有一个特殊的信号<code>SIGKILL</code>是无法被捕获的，也就是我们经常使用的<code>kill -9 &lt;pid&gt;</code>发出的信号。</p>
<p>通过<code>man signal</code>能查看到信号的更多详细信息，<code>kill -l</code>显示出信号列表，然后<code>kill -&lt;信号名称&gt; &lt;pid&gt;</code>发送某个信号。</p>
<p>我们可以使用命令加一个后缀<code>&amp;</code>来让该命令在后台运行，然后通过<code>jobs</code>命令能列出当前shell会话中未完成的全部任务。但后台进程仍然是当前终端会话进程的子进程，一旦关闭终端就会发送一个信号<code>SIGHUP</code>导致这些后台的进程被终止掉，命令前加<code>nohup</code>就是用来防止这种情况的。
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-sh" data-lang="sh">$ sleep <span style="color:#ae81ff">1000</span>
^Z      <span style="color:#75715e"># 使用ctrl+Z发送了一个SIGTSTP信号从而暂停了任务</span>
<span style="color:#f92672">[</span>1<span style="color:#f92672">]</span>  + <span style="color:#ae81ff">18653</span> suspended  sleep <span style="color:#ae81ff">1000</span>

$ nohup sleep <span style="color:#ae81ff">2000</span> &amp;
<span style="color:#f92672">[</span>2<span style="color:#f92672">]</span> <span style="color:#ae81ff">18745</span>
appending output to nohup.out

$ jobs
<span style="color:#f92672">[</span>1<span style="color:#f92672">]</span>  + suspended  sleep <span style="color:#ae81ff">1000</span>
<span style="color:#f92672">[</span>2<span style="color:#f92672">]</span>  - running    nohup sleep <span style="color:#ae81ff">2000</span>

$ bg %1     <span style="color:#75715e"># 后台接着启动暂停中的1号任务，也可以使用fg来前台启动</span>
<span style="color:#f92672">[</span>1<span style="color:#f92672">]</span>  - <span style="color:#ae81ff">18653</span> continued  sleep <span style="color:#ae81ff">1000</span>

$ jobs
<span style="color:#f92672">[</span>1<span style="color:#f92672">]</span>  - running    sleep <span style="color:#ae81ff">1000</span>
<span style="color:#f92672">[</span>2<span style="color:#f92672">]</span>  + running    nohup sleep <span style="color:#ae81ff">2000</span>

$ kill -SIGHUP %1   <span style="color:#75715e"># 也可以使用%任务编号的形式，不一定要用PID</span>
<span style="color:#f92672">[</span>1<span style="color:#f92672">]</span>  + <span style="color:#ae81ff">18653</span> hangup     sleep <span style="color:#ae81ff">1000</span>

$ jobs
<span style="color:#f92672">[</span>2<span style="color:#f92672">]</span>  + running    nohup sleep <span style="color:#ae81ff">2000</span></code></pre></div></p>
<h2 id="常用工具">常用工具</h2>
<p>可以使用<code>man</code>程序来查看一些其他shell程序的使用方法和参数，例如<code>man ls</code>。</p>
<p>常用的开发编译相关工具可参考<a href="/docs/other/tools">这篇文章</a>。</p>
<h3 id="find">find</h3>
<p>使用<code>find 起始目录 参数</code>可以方便的查找文件，常用的参数为:</p>
<ul>
<li>-name，按文件名，-iname也是按文件名但可忽略大小写</li>
<li>-type，按文件类型，d为目录，f为文件</li>
<li>-perm，按权限，例如0755</li>
<li>-user，按所属用户查找</li>
<li>-group，按所属用户组查找</li>
<li>-mtime，按修改时间查找，+表示以前，-表示以内，在mac下，<code>-1d2h</code>表示1天2小时以内修改过的文件，在linux下只能跟一个数字，例如<code>-1</code>表示1天内，如果需要多少分钟内应该用-mmin</li>
<li>-size，按大小查找，+表示大于，-表示小于，可以跟单位k、M、G、T</li>
<li>-maxdepth，指定最大搜索深度</li>
<li>-executable，可执行文件</li>
<li>-exec，执行相关命令</li>
</ul>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-sh" data-lang="sh"><span style="color:#75715e"># 文件名匹配ind*.html的文件</span>
➜  hjlarry.github.io git:<span style="color:#f92672">(</span>master<span style="color:#f92672">)</span> ✗ find . -iname ind*.html
./tags/index.html
./docs/sicp/software-introduction/index.html
<span style="color:#75715e"># 文件修改时间在2小时内的文件</span>
➜  hjlarry.github.io git:<span style="color:#f92672">(</span>master<span style="color:#f92672">)</span> ✗ find . -type f -mmin -120 | xargs ls -lh
-rw-rw-rw- <span style="color:#ae81ff">1</span> hejl hejl   <span style="color:#ae81ff">98</span> May <span style="color:#ae81ff">24</span> 10:12 ./.git/FETCH_HEAD
-rw-r--r-- <span style="color:#ae81ff">1</span> hejl hejl   <span style="color:#ae81ff">41</span> May <span style="color:#ae81ff">24</span> 10:12 ./.git/ORIG_HEAD
<span style="color:#75715e"># 文件大小大于2M的文件</span>
➜  hjlarry.github.io git:<span style="color:#f92672">(</span>master<span style="color:#f92672">)</span> ✗ find . -type f -size +2M | xargs ls -lh
-r--r--r-- <span style="color:#ae81ff">1</span> hejl hejl 5.7M Apr  <span style="color:#ae81ff">4</span> 17:41 ./.git/modules/themes/book/objects/pack/pack-3a8d2ad7db7fe81a41409e60d27dce22b77b6c68.pack
-r--r--r-- <span style="color:#ae81ff">1</span> hejl hejl 3.0M Apr <span style="color:#ae81ff">20</span> 14:36 ./.git/objects/pack/pack-472a07bf934d028116265bd13cbea79d367c5e90.pack</code></pre></div>
<h3 id="grep">grep</h3>
<p>使用该命令可以从文件或管道中查找具体的内容，其支持多文件名，支持通配符，内容匹配时支持正则表达式。常用的参数为:</p>
<ul>
<li>-r，递归搜索所有子目录</li>
<li>-l，仅输出文件名</li>
<li>-n，输出行号</li>
<li>-c，按文件统计匹配次数</li>
<li>-v，排除指定内容</li>
<li>-i，忽略大小写</li>
<li>-e，多模式匹配，命中其一即可</li>
<li>-An，输出其后n行内容</li>
<li>-Bn，输出其前n行内容</li>
<li>-Cn，输出其前后n行内容</li>
</ul>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-sh" data-lang="sh"><span style="color:#f92672">[</span>ubuntu<span style="color:#f92672">]</span> ~/.mac $ grep -c <span style="color:#e6db74">&#34;main&#34;</span> *
alpine.sh:0
grep: assem: Is a directory
test.c:1
test.go:2
tt:17
<span style="color:#f92672">[</span>ubuntu<span style="color:#f92672">]</span> ~/.mac $ grep -n -i <span style="color:#e6db74">&#34;\bmain&#34;</span> *.go
1:package main
5:func main<span style="color:#f92672">()</span> <span style="color:#f92672">{</span>
<span style="color:#f92672">[</span>ubuntu<span style="color:#f92672">]</span> ~/.mac $ objdump -d -M intel test | grep -A3 <span style="color:#e6db74">&#34;&lt;main.main&gt;&#34;</span>
<span style="color:#ae81ff">0000000000452330</span> &lt;main.main&gt;:
  452330:	<span style="color:#ae81ff">64</span> <span style="color:#ae81ff">48</span> 8b 0c <span style="color:#ae81ff">25</span> f8 ff 	mov    rcx,QWORD PTR fs:0xfffffffffffffff8
  452337:	ff ff
  452339:	<span style="color:#ae81ff">48</span> 3b <span style="color:#ae81ff">61</span> <span style="color:#ae81ff">10</span>          	cmp    rsp,QWORD PTR <span style="color:#f92672">[</span>rcx+0x10<span style="color:#f92672">]</span>
--
  4523ae:	eb <span style="color:#ae81ff">80</span>                	jmp    <span style="color:#ae81ff">452330</span> &lt;main.main&gt;</code></pre></div>
</article>
 
      

      <footer class="book-footer">
        
  <div class="flex justify-between">



  <div>
    
    <a class="flex align-center" href="https://github.com/hjlarry/hjlarry.github.io/commit/ae2e6f7fd19e805816bebc3dc6139647e6c39e4c" title='最后修改者 hjlarry | July 16, 2020' target="_blank" rel="noopener">
      <img src="/svg/calendar.svg" class="book-icon" alt="Calendar" />
      <span>July 16, 2020</span>
    </a>
  </div>



  <div>
    <a class="flex align-center" href="https://github.com/hjlarry/hjlarry.github.io/edit/master/content/docs/other/shell/_index.md" target="_blank" rel="noopener">
      <img src="/svg/edit.svg" class="book-icon" alt="Edit" />
      <span>编辑本页</span>
    </a>
  </div>

</div>

 
        <br>
<div style="text-align: center;font-size:xx-small;">
    Powered by <a href="https://gohugo.io" target="_blank">Hugo</a> | Theme by <a
        href="https://github.com/alex-shpak/hugo-book" target="_blank">hugo-book</a>
</div>
      </footer>

      
  
 

      <label for="menu-control" class="hidden book-menu-overlay"></label>
    </div>

    
    <aside class="book-toc">
      
  <nav id="TableOfContents">
  <ul>
    <li><a href="#什么是shell">什么是shell</a></li>
    <li><a href="#基础使用">基础使用</a>
      <ul>
        <li><a href="#基础技巧">基础技巧</a></li>
        <li><a href="#程序间的连接">程序间的连接</a></li>
        <li><a href="#root">root</a></li>
      </ul>
    </li>
    <li><a href="#编写脚本">编写脚本</a>
      <ul>
        <li><a href="#变量">变量</a></li>
        <li><a href="#函数">函数</a></li>
        <li><a href="#命令替换">命令替换</a></li>
        <li><a href="#控制流">控制流</a></li>
        <li><a href="#通配符">通配符</a></li>
        <li><a href="#脚本">脚本</a></li>
      </ul>
    </li>
    <li><a href="#任务控制">任务控制</a></li>
    <li><a href="#常用工具">常用工具</a>
      <ul>
        <li><a href="#find">find</a></li>
        <li><a href="#grep">grep</a></li>
      </ul>
    </li>
  </ul>
</nav>

 
    </aside>
    
  </main>

  
</body>

</html>












